library lib_ui;

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:visibility_detector/visibility_detector.dart';

class SwiperController {
  final List<_SwiperState> _swiperStatesList = [];

  void startAutoPlayTimer() {
    for (var element in _swiperStatesList) {
      element.startAutoPlayTimer();
    }
  }

  void stopAutoPlayTimer() {
    for (var element in _swiperStatesList) {
      element.stopAutoPlayTimer();
    }
  }

  void _attach(_SwiperState state) {
    if (_swiperStatesList.contains(state)) {
      return;
    }

    _swiperStatesList.add(state);
  }

  void _detach(_SwiperState state) {
    _swiperStatesList.remove(state);
  }
}

class Swiper extends StatefulWidget {
  final int itemCount;
  final int initialPage;
  final Widget Function(int index) itemBuild;
  final SwiperController? controller;
  final Duration duration;
  final bool autoPlay;
  final Duration autoPlayInterval;

  const Swiper({
    Key? key,
    required this.itemCount,
    required this.initialPage,
    required this.itemBuild,
    this.controller,
    this.duration = const Duration(milliseconds: 600),
    this.autoPlay = true,
    this.autoPlayInterval = const Duration(seconds: 8),
  }) : super(key: key);

  @override
  State<Swiper> createState() => _SwiperState();
}

class _SwiperState extends State<Swiper> with WidgetsBindingObserver {
  int _currentPage = 0;
  late PageController _pageController;
  Timer? _autoPlayTimer;

  @override
  void initState() {
    super.initState();

    widget.controller?._attach(this);

    _currentPage = widget.initialPage;
    _pageController = PageController(
      initialPage: widget.itemCount > 1 ? widget.initialPage + 1 : _currentPage,
    );

    startAutoPlayTimer();
  }

  @override
  void dispose() {
    super.dispose();

    widget.controller?._detach(this);

    stopAutoPlayTimer();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed: //onResume
        startAutoPlayTimer();
        break;
      case AppLifecycleState.paused: //onPause
        stopAutoPlayTimer();
        break;
      default:
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return VisibilityDetector(
        key: const ValueKey("VisibilityDetector"),
        onVisibilityChanged: _onVisibilityChanged,
        child: NotificationListener<ScrollNotification>(
          onNotification: _onScrollNotification,
          child: Stack(
            children: [
              PageView.builder(
                onPageChanged: _onPageChanged,
                controller: _pageController,
                itemCount: widget.itemCount > 1 ? widget.itemCount + 2 : 1,
                itemBuilder: (buildContext, index) {
                  if (widget.itemCount > 1) {
                    if (index == 0) {
                      return widget.itemBuild(widget.itemCount - 1);
                    } else if (index == widget.itemCount + 1) {
                      return widget.itemBuild(0);
                    } else {
                      return _buildPageViewItem(index - 1);
                    }
                  } else {
                    return _buildPageViewItem(index);
                  }
                },
              ),
              if (widget.itemCount > 1)
                Positioned(
                  bottom: 8,
                  left: 0,
                  right: 0,
                  child: _buildIndicator(),
                ),
            ],
          ),
        ));
  }

  bool _onScrollNotification(ScrollNotification notification) {
    if (notification is UserScrollNotification) {
      notification.direction != ScrollDirection.idle
          ? stopAutoPlayTimer()
          : startAutoPlayTimer();
    }

    return false;
  }

  void _onVisibilityChanged(VisibilityInfo info) {
    if (info.visibleFraction == 0) {
      stopAutoPlayTimer();
    } else if (info.visibleFraction == 1) {
      startAutoPlayTimer();
    }
  }

  void _onPageChanged(index) async {
    int currentPage;
    if (widget.itemCount > 1) {
      if (index == 0) {
        currentPage = widget.itemCount - 1;
        await Future.delayed(widget.duration);
        _pageController.jumpToPage(widget.itemCount);
      } else if (index == widget.itemCount + 1) {
        currentPage = 0;
        await Future.delayed(widget.duration);
        _pageController.jumpToPage(1);
      } else {
        currentPage = index - 1;
      }
    } else {
      currentPage = index;
    }

    setState(() {
      _currentPage = currentPage;
    });
  }

  Widget _buildPageViewItem(int index) {
    return widget.itemBuild(index);
  }

  Widget _buildIndicator() {
    return Container(
      padding: EdgeInsets.zero,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: List.generate(widget.itemCount, (index) {
          return Container(
            margin: const EdgeInsets.symmetric(horizontal: 5),
            width: 4,
            height: 4,
            decoration: BoxDecoration(
                color: index == _currentPage
                    ? const Color(0xFF47C7E3)
                    : const Color(0xFFC4C4C4),
                borderRadius: const BorderRadius.all(Radius.circular(2))),
          );
        }),
      ),
    );
  }

  void startAutoPlayTimer() {
    if (!widget.autoPlay || widget.itemCount <= 1 || _autoPlayTimer != null) {
      return;
    }

    _autoPlayTimer = Timer.periodic(widget.autoPlayInterval, (timer) {
      if (_pageController.hasClients) {
        _pageController.nextPage(
          duration: widget.duration,
          curve: Curves.ease,
        );
      }
    });
  }

  void stopAutoPlayTimer() {
    _autoPlayTimer?.cancel();
    _autoPlayTimer = null;
  }
}
